////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2003-2006 Adobe Macromedia Software LLC and its licensors.
//  All Rights Reserved. The following is Source Code and is subject to all
//  restrictions on such code as contained in the End User License Agreement
//  accompanying this product.
//
////////////////////////////////////////////////////////////////////////////////

package mx.charts
{

import mx.charts.chartClasses.CartesianChart;
import mx.charts.chartClasses.DataTip;
import mx.charts.chartClasses.DataTransform;
import mx.charts.chartClasses.Series;
import mx.charts.series.BarSeries;
import mx.charts.series.BarSet;
import mx.charts.styles.HaloDefaults;
import mx.core.mx_internal;
import mx.graphics.SolidColor;
import mx.graphics.Stroke;
import mx.styles.CSSStyleDeclaration;

import flash.filters.DropShadowFilter;

use namespace mx_internal;

//--------------------------------------
//  Styles
//--------------------------------------

/**
 *  Specifies how wide to draw the bars relative to the category width,
 *  as a percentage in the range of 0 to 1. 
 *  A value of 1 uses the entire space, while a value of 0.6
 *  uses 60% of the bar's available space.  
 *  The actual bar width used is the smaller of the
 *  <code>barWidthRatio</code> property and the
 *  <code>maxbarWidth</code> property
 *  Clustered bars divide this space proportionally
 *  among the bars in each cluster.
 *  The default value is 0.65.
 */
[Style(name="barWidthRatio", type="Number", inherit="no")]

/**
 *  Specifies how wide to draw the bars, in pixels.
 *  The actual bar width used is the smaller of this property
 *  and the <code>barWidthRatio</code> property.
 *  Clustered bars divide this space proportionally
 *  among the bars in each cluster. 
 */
[Style(name="maxBarWidth", type="Number", format="Length", inherit="no")]

//--------------------------------------
//  Other metadata
//--------------------------------------

[DefaultBindingProperty(destination="dataProvider")]

[DefaultTriggerEvent("itemClick")]

[IconFile("BarChart.png")]

/**
 *  The BarChart control represents data as a series of horizontal bars
 *  whose length is determined by values in the data.
 *  A BarChart control can represent different chart variations,
 *  including simple bars, clustered bars, stacked, 100% stacked, and high/low.
 *  
 *  <p>The BarChart control expects its <code>series</code> property
 *  to contain an array of BarSeries objects.</p>
 *  
 *  <p>Stacked and 100% bar charts override the <code>minField</code>
 *  property of their BarSeries objects.</p>
 *
 *  @mxml
 *  
 *  <p>The <code>&lt;mx:BarChart&gt;</code> tag inherits all the properties
 *  of its parent classes, and adds the following properties:</p>
 *  
 *  <pre>
 *  &lt;mx:BarChart
 *    <strong>Properties</strong>
 *    type="clustered|overlaid|stacked|100%"
 *    
 *    <strong>Styles</strong>
 *    barWidthRatio=".65"
 *    maxBarWidth="<i>No default</i>"
 *  /&gt;
 *  </pre>
 *  
 *  @includeExample examples/Column_BarChartExample.mxml
 *  
 *  @see mx.charts.series.BarSeries
 */
public class BarChart extends CartesianChart
{
    include "../core/Version.as";

	//--------------------------------------------------------------------------
	//
	//  Class initialization
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 */
	private static var stylesInited:Boolean = initStyles();	
	
	/**
	 *  @private
	 */
	private static function initStyles():Boolean
	{
		HaloDefaults.init();

		var barChartStyle:CSSStyleDeclaration =
			HaloDefaults.createSelector("BarChart");

		barChartStyle.defaultFactory = function():void
		{
			this.axisColor = 0xD5DEDD;
			this.barWidthRatio = 0.65;
			this.chartSeriesStyles = HaloDefaults.chartBaseChartSeriesStyles;		
			this.dataTipRenderer = DataTip;
			this.fill = new SolidColor(0xFFFFFF, 0);
			this.calloutStroke = new Stroke(0x888888,2);			
			this.fontFamily = "Arial";
			this.fontSize = 10;
			this.gridLinesStyleName = "horizontalGridLines";
			this.horizontalAxisStyleName = "blockNumericAxis";
			this.secondHorizontalAxisStyleName = "blockNumericAxis";
			this.secondVerticalAxisStyleName = "blockCategoryAxis";
			this.textAlign = "left";			
			this.verticalAxisStyleName = "blockCategoryAxis";
		}

		return true;
	}

	//--------------------------------------------------------------------------
	//
	//  Class constants
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 */
	private static var INVALIDATING_STYLES:Object =
	{
		barWidthRatio: 1,
		maxBarWidth: 1
	}
	
	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------

	/**
	 *  Constructor.
	 */
	public function BarChart()
	{
		super();

		LinearAxis(verticalAxis).autoAdjust = false;

		dataTipMode = "single";

		seriesFilters = [ new DropShadowFilter(2, 45, 0.2 * 0xFFFFFF)];
	}

	//--------------------------------------------------------------------------
	//
	//  Variables
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 */
	private var _perSeriesBarWidthRatio:Number;
	
	/**
	 *  @private
	 */
	private var _perSeriesMaxBarWidth:Number;
	
	/**
	 *  @private
	 */
	private var _rightOffset:Number;
	
	/**
	 *  @private
	 */
	private var _wasStacked:Boolean = false;
	
	//--------------------------------------------------------------------------
	//
	//  Properties
	//
	//--------------------------------------------------------------------------
	
    //----------------------------------
	//  type
    //----------------------------------

	/**
	 *  @private
	 *  Storage for the type property.
	 */
	private var _type:String = "clustered";

	[Inspectable(category="General", enumeration="stacked,100%,clustered,overlaid", defaultValue="clustered")]

	/**
	 *  The type of bar chart to render. Possible values are:    
	 *  <ul>
	 *    <li><code>"clustered"</code>:
	 *    Bars are grouped by category.
	 *    This is the default value.</li>
	 *  
	 *    <li><code>"overlaid"</code>:
	 *    Multiple bars are rendered on top of each other by category,
	 *    with the last series specified on top.</li>
	 *  
	 *    <li><code>"stacked"</code>:
	 *    Bars are stacked end to end and grouped by category.
	 *    Each bar represents the cumulative value of the values beneath it.</li>
	 *  
	 *    <li><code>"100%"</code>:
	 *    Bars are stacked end to end, adding up to 100%.
	 *    Each bar represents the percent that it contributes
	 *    to the sum of the values for that category.</li>
	 *  </ul>
	 */
	public function get type():String
	{
		return _type;
	}

	/**
	 *  @private
	 */
	public function set type(value:String):void
	{
		_type = value;

		invalidateSeries();
		invalidateData();
	}
	
	//--------------------------------------------------------------------------
	//
	//  Overridden methods: UIComponent
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 */
	override public function styleChanged(styleProp:String):void
	{
		if (styleProp == null || INVALIDATING_STYLES[styleProp] != undefined)
			invalidateSeries();

		super.styleChanged(styleProp);
	}

	//--------------------------------------------------------------------------
	//
	//  Overridden methods: ChartBase
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 */
	override protected function customizeSeries(seriesGlyph:Series, i:uint):void
	{
		if ((seriesGlyph is BarSeries) || (seriesGlyph is BarSet))
		{
			var series:Object = seriesGlyph;

			if (!isNaN(_perSeriesBarWidthRatio))
				series.barWidthRatio = _perSeriesBarWidthRatio;
	
			if (!isNaN(_perSeriesMaxBarWidth))
				series.maxBarWidth = _perSeriesMaxBarWidth;
				
			if (_type == "overlaid")		
				series.offset = 0;
			else
				series.offset = _rightOffset - i * _perSeriesBarWidthRatio;
			
			if (series is BarSeries)
			{
				series.stacker = null;
				series.stackTotals = null;
			}
		}
	}
	
	/**
	 *  @private
	 */
	override protected function applySeriesSet(seriesSet:Array,
											   transform:DataTransform):Array
	{
		
		var barWidthRatio:Number = getStyle("barWidthRatio");
		var maxBarWidth:Number = getStyle("maxBarWidth");

		switch (_type)
		{
			case "stacked":
			case "100%":
			{
				_wasStacked = true;

				for (var i:int = 0; i < seriesSet.length; i++)
				{
					seriesSet[i].offset = 0;
				}

				var newSeriesGlyph:BarSet = new BarSet();
				newSeriesGlyph.series = seriesSet;

				if (!isNaN(barWidthRatio))
					newSeriesGlyph.barWidthRatio = barWidthRatio;

				if (!isNaN(maxBarWidth))
					newSeriesGlyph.maxBarWidth = maxBarWidth;

				newSeriesGlyph.type = _type;

				invalidateData();
				return [ newSeriesGlyph ];
			}
							
			case "clustered":
			default:
			{
				_perSeriesBarWidthRatio = barWidthRatio / seriesSet.length;
				_perSeriesMaxBarWidth = maxBarWidth / seriesSet.length;
				_rightOffset = barWidthRatio / 2 - _perSeriesBarWidthRatio / 2;
				return super.applySeriesSet(seriesSet, transform);
			}

			case "overlaid":
			{
				_perSeriesBarWidthRatio = barWidthRatio;
				_perSeriesMaxBarWidth = maxBarWidth;

				_rightOffset = 0;
				super.applySeriesSet(seriesSet, transform);
				break;
			}
		}

		return seriesSet;
	}
				
	//--------------------------------------------------------------------------
	//
	//  Overridden methods: CartesianChart
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 */
	override protected function initSecondaryMode():void
	{
		super.initSecondaryMode();
		
		if (!secondHorizontalAxis)
			secondHorizontalAxis = new LinearAxis();

		if (!secondHorizontalAxisRenderer)
			secondHorizontalAxisRenderer = new AxisRenderer();			
	}
}

}
